home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 10868 / 10868.xpi / modules / engines / tabs.js < prev   
Text File  |  2010-02-02  |  9KB  |  280 lines

  1. /* ***** BEGIN LICENSE BLOCK *****
  2.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3.  *
  4.  * The contents of this file are subject to the Mozilla Public License Version
  5.  * 1.1 (the "License"); you may not use this file except in compliance with
  6.  * the License. You may obtain a copy of the License at
  7.  * http://www.mozilla.org/MPL/
  8.  *
  9.  * Software distributed under the License is distributed on an "AS IS" basis,
  10.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11.  * for the specific language governing rights and limitations under the
  12.  * License.
  13.  *
  14.  * The Original Code is Bookmarks Sync.
  15.  *
  16.  * The Initial Developer of the Original Code is Mozilla.
  17.  * Portions created by the Initial Developer are Copyright (C) 2008
  18.  * the Initial Developer. All Rights Reserved.
  19.  *
  20.  * Contributor(s):
  21.  *  Myk Melez <myk@mozilla.org>
  22.  *  Jono DiCarlo <jdicarlo@mozilla.com>
  23.  *
  24.  * Alternatively, the contents of this file may be used under the terms of
  25.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  26.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  27.  * in which case the provisions of the GPL or the LGPL are applicable instead
  28.  * of those above. If you wish to allow use of your version of this file only
  29.  * under the terms of either the GPL or the LGPL, and not to allow others to
  30.  * use your version of this file under the terms of the MPL, indicate your
  31.  * decision by deleting the provisions above and replace them with the notice
  32.  * and other provisions required by the GPL or the LGPL. If you do not delete
  33.  * the provisions above, a recipient may use your version of this file under
  34.  * the terms of any one of the MPL, the GPL or the LGPL.
  35.  *
  36.  * ***** END LICENSE BLOCK ***** */
  37.  
  38. const EXPORTED_SYMBOLS = ['TabEngine'];
  39.  
  40. const Cc = Components.classes;
  41. const Ci = Components.interfaces;
  42. const Cu = Components.utils;
  43.  
  44. Cu.import("resource://gre/modules/XPCOMUtils.jsm");
  45. Cu.import("resource://weave/util.js");
  46. Cu.import("resource://weave/engines.js");
  47. Cu.import("resource://weave/stores.js");
  48. Cu.import("resource://weave/trackers.js");
  49. Cu.import("resource://weave/type_records/tabs.js");
  50. Cu.import("resource://weave/engines/clientData.js");
  51.  
  52. function TabEngine() {
  53.   this._init();
  54. }
  55. TabEngine.prototype = {
  56.   __proto__: SyncEngine.prototype,
  57.   name: "tabs",
  58.   _displayName: "Tabs",
  59.   description: "Access tabs from other devices via the History menu",
  60.   logName: "Tabs",
  61.   _storeObj: TabStore,
  62.   _trackerObj: TabTracker,
  63.   _recordObj: TabSetRecord,
  64.  
  65.   _init: function _init() {
  66.     // Reset the client on every startup so that we fetch recent tabs
  67.     SyncEngine.prototype._init.call(this);
  68.     this._resetClient();
  69.   },
  70.  
  71.   // API for use by Weave UI code to give user choices of tabs to open:
  72.   getAllClients: function TabEngine_getAllClients() {
  73.     return this._store._remoteClients;
  74.   },
  75.  
  76.   getClientById: function TabEngine_getClientById(id) {
  77.     return this._store._remoteClients[id];
  78.   },
  79.  
  80.   _resetClient: function TabEngine__resetClient() {
  81.     SyncEngine.prototype._resetClient.call(this);
  82.     this._store.wipe();
  83.   },
  84.  
  85.   _syncFinish: function _syncFinish() {
  86.     SyncEngine.prototype._syncFinish.call(this);
  87.     this._tracker.resetChanged();
  88.   },
  89.  
  90.   /* The intent is not to show tabs in the menu if they're already
  91.    * open locally.  There are a couple ways to interpret this: for
  92.    * instance, we could do it by removing a tab from the list when
  93.    * you open it -- but then if you close it, you can't get back to
  94.    * it.  So the way I'm doing it here is to not show a tab in the menu
  95.    * if you have a tab open to the same URL, even though this means
  96.    * that as soon as you navigate anywhere, the original tab will
  97.    * reappear in the menu.
  98.    */
  99.   locallyOpenTabMatchesURL: function TabEngine_localTabMatches(url) {
  100.     return this._store.getAllTabs().some(function(tab) {
  101.       return tab.urlHistory[0] == url;
  102.     });
  103.   }
  104. };
  105.  
  106.  
  107. function TabStore() {
  108.   this._TabStore_init();
  109. }
  110. TabStore.prototype = {
  111.   __proto__: Store.prototype,
  112.   name: "tabs",
  113.   _logName: "Tabs.Store",
  114.   _remoteClients: {},
  115.  
  116.   _TabStore_init: function TabStore__init() {
  117.     this._init();
  118.   },
  119.  
  120.   get _sessionStore() {
  121.     let sessionStore = Cc["@mozilla.org/browser/sessionstore;1"].
  122.                getService(Ci.nsISessionStore);
  123.     this.__defineGetter__("_sessionStore", function() { return sessionStore;});
  124.     return this._sessionStore;
  125.   },
  126.  
  127.   itemExists: function TabStore_itemExists(id) {
  128.     return id == Clients.clientID;
  129.   },
  130.  
  131.  
  132.   getAllTabs: function getAllTabs(filter) {
  133.     let filteredUrls = new RegExp(Svc.Prefs.get("engine.tabs.filteredUrls"), "i");
  134.  
  135.     // Iterate through each tab of each window
  136.     let allTabs = [];
  137.     let wins = Svc.WinMediator.getEnumerator("navigator:browser");
  138.     while (wins.hasMoreElements()) {
  139.       // Get the tabs for both Firefox and Fennec
  140.       let window = wins.getNext();
  141.       let tabs = window.gBrowser && window.gBrowser.tabContainer.childNodes;
  142.       tabs = tabs || window.Browser._tabs;
  143.   
  144.       // Extract various pieces of tab data
  145.       Array.forEach(tabs, function(tab) {
  146.         let browser = tab.linkedBrowser || tab.browser;
  147.         let url = browser.currentURI.spec;
  148.  
  149.         // Filter out some urls if necessary
  150.         if (filter && filteredUrls.test(url))
  151.           return;
  152.  
  153.         allTabs.push({
  154.           title: browser.contentTitle || "",
  155.           urlHistory: [url],
  156.           icon: browser.mIconURL || "",
  157.           lastUsed: tab.lastUsed || 0
  158.         });
  159.       });
  160.     }
  161.   
  162.     return allTabs;
  163.   },
  164.  
  165.   createRecord: function TabStore_createRecord(id, cryptoMetaURL) {
  166.     let record = new TabSetRecord();
  167.     record.clientName = Clients.clientName;
  168.  
  169.     // Sort tabs in descending-used order to grab the most recently used
  170.     record.tabs = this.getAllTabs(true).sort(function(a, b) {
  171.       return b.lastUsed - a.lastUsed;
  172.     }).slice(0, 25);
  173.     record.tabs.forEach(function(tab) {
  174.       this._log.trace("Wrapping tab: " + JSON.stringify(tab));
  175.     }, this);
  176.  
  177.     record.id = id;
  178.     record.encryption = cryptoMetaURL;
  179.     return record;
  180.   },
  181.  
  182.   getAllIDs: function TabStore_getAllIds() {
  183.     let ids = {};
  184.     ids[Clients.clientID] = true;
  185.     return ids;
  186.   },
  187.  
  188.   wipe: function TabStore_wipe() {
  189.     this._remoteClients = {};
  190.   },
  191.  
  192.   create: function TabStore_create(record) {
  193.     this._log.debug("Adding remote tabs from " + record.clientName);
  194.     this._remoteClients[record.id] = record.cleartext;
  195.  
  196.     // Lose some precision, but that's good enough (seconds)
  197.     let roundModify = Math.floor(record.modified / 1000);
  198.     let notifyState = Svc.Prefs.get("notifyTabState");
  199.     // If there's no existing pref, save this first modified time
  200.     if (notifyState == null)
  201.       Svc.Prefs.set("notifyTabState", roundModify);
  202.     // Don't change notifyState if it's already 0 (don't notify)
  203.     else if (notifyState == 0)
  204.       return;
  205.     // We must have gotten a new tab that isn't the same as last time
  206.     else if (notifyState != roundModify)
  207.       Svc.Prefs.set("notifyTabState", 0);
  208.   }
  209. };
  210.  
  211.  
  212. function TabTracker() {
  213.   this._TabTracker_init();
  214. }
  215. TabTracker.prototype = {
  216.   __proto__: Tracker.prototype,
  217.   name: "tabs",
  218.   _logName: "TabTracker",
  219.   file: "tab_tracker",
  220.  
  221.   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
  222.  
  223.   _TabTracker_init: function TabTracker__init() {
  224.     this._init();
  225.     this.resetChanged();
  226.  
  227.     // Make sure "this" pointer is always set correctly for event listeners
  228.     this.onTab = Utils.bind2(this, this.onTab);
  229.  
  230.     // Register as an observer so we can catch windows opening and closing:
  231.     Svc.WinWatcher.registerNotification(this);
  232.  
  233.     // Also register listeners on already open windows
  234.     let wins = Svc.WinMediator.getEnumerator("navigator:browser");
  235.     while (wins.hasMoreElements())
  236.       this._registerListenersForWindow(wins.getNext());
  237.   },
  238.  
  239.   _registerListenersForWindow: function TabTracker__registerListen(window) {
  240.     this._log.trace("Registering tab listeners in new window");
  241.  
  242.     // For each topic, add or remove onTab as the listener
  243.     let topics = ["TabOpen", "TabClose", "TabSelect"];
  244.     let onTab = this.onTab;
  245.     let addRem = function(add) topics.forEach(function(topic) {
  246.       window[(add ? "add" : "remove") + "EventListener"](topic, onTab, false);
  247.     });
  248.  
  249.     // Add the listeners now and remove them on unload
  250.     addRem(true);
  251.     window.addEventListener("unload", function() addRem(false), false);
  252.   },
  253.  
  254.   observe: function TabTracker_observe(aSubject, aTopic, aData) {
  255.     // Add tab listeners now that a window has opened
  256.     let window = aSubject.QueryInterface(Ci.nsIDOMWindow);
  257.     if (aTopic == "domwindowopened") {
  258.       let self = this;
  259.       window.addEventListener("load", function() {
  260.         // Only register after the window is done loading to avoid unloads
  261.         self._registerListenersForWindow(window);
  262.       }, false);
  263.     }
  264.   },
  265.  
  266.   onTab: function onTab(event) {
  267.     this._log.trace(event.type);
  268.     this.score += 1;
  269.     this._changedIDs[Clients.clientID] = true;
  270.  
  271.     // Store a timestamp in the tab to track when it was last used
  272.     event.originalTarget.lastUsed = Math.floor(Date.now() / 1000);
  273.   },
  274.  
  275.   get changedIDs() this._changedIDs,
  276.  
  277.   // Provide a way to empty out the changed ids
  278.   resetChanged: function resetChanged() this._changedIDs = {}
  279. }
  280.